<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>变量、作用域和内存问题</title>
</head>
<body>
    <script>
        "use strict";
        /*
            按照ECMA-262的定义，JavaScript的变量与其他语言的变量有很大区别。JavaScript变量松散类型的本质，决定了他只是在特定的时间用于保存特定的值得一个名字而已。
            由于不存在定义某个变量必须要保存何种数据类型的规则，变量的值及其数据类型可以在脚本的生命周期内改变。尽管从某种角度看，这个能是一个既有趣有强大，同时又
            容易出现问题的特性，但JavaScript变量实际的复杂程度还远不如此。

            基本类型和引用类型的值：
                ECMAScript变量可能包含两种不同数据类型的值：基本类型值和引用类型值。基本类型值指的是简单的数据段，而引用类型值指那些可能由多个值构成的对象。

                在将一个值赋给变量时，编译器必须确定这个值是基本类型值还是引用类型值。前面讨论了5中基本数据类型：Undefined、Null、Boolean、Number和String。
                这5中基本数据类型是按值访问的，因为可以操作保存在变量中的实际的值。

                引用类型的值是保存在内存中的对象。与其他语言不同，JavaScript不允许直接访问内存中的位置，也就是说不能直接操作对象的内存空间。在操作对象时，实际上
                是在操作对象的引用而不是实际的对象。为此，引用类型的值是按引用访问的（这种说法不严密，当复制保存着对象的某个变量时，操作的是对象的引用。但在为对象
                添加属性时，操作的是实际对象）。

                1、动态的属性
                    定义基本类型值和引用类型值的方式是类似的：创建一个变量并为该变量赋值。但是，当这个值保存到变量当中以后，对不同类型的值可以执行的操作大相径庭。
                    对于引用类型的值，我们可以为其添加属性和方法，也可以改变和删除其属性和方法。如下代码：
                        var person = new Object();
                        person.name = "Nicholas";
                        alert(person.name); // Nicholas

                    以上代码创建一个变量并将其保存在变量person中。然后，我们为该对象添加了一个名为name的属性，并将字符串值"Nicholas"赋给了这个属性。紧接着，又通过
                    alert()函数访问了这个新属性。如果对象不被销毁或者这个属性不被删除，则这个属性将一直存在。

                    但是，我们不能给基本类型的值添加属性，尽管这样做不会导致任何错误。比如：
                        var name = "Nicholas";
                        name.age = 18;
                        alert(name.age); // undefined

                2、复制变量值
                    除了保存的方式不同之外，在从一个变量向另一个变量复制基本类型值和引用类型值时，也存在不同。如果从一个变量向另一个变量复制基本类型值，会在变量对象上
                    创建一个新值，然后把该值复制到为新变量分配的位置上。基本类型的值复制，两个变量的值互不影响。

                    当从一个变量向另一个变量复制引用类型的值时，同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是，这个值的副本实际上是一个指针，
                    而这个指针指向存储在堆中的一个对象。复制操作结束后，两个变量实际上将引用同一个对象。因此，改变其中一个变量，就会影响另一个变量。

                3、传递参数
                    ECMAScript中所有函数的参数都是按值传递的。也就是说，把函数外部的值复制给函数内部的参数，就和把值从一个变量复制到另一个变量一样。基本类型的值传递
                    就如同基本类型变量的复制一样，而引用类型值的传递，则如同引用类型变量的复制一样。

                    在向参数传递基本类型值时，被传递的值会被复制给一个局部变量（即命名参数，或者用ECMAScript的概念来说，就是arguments对象中的一个元素）。在向参数
                    传递引用类型的值时，会把这个值在内存中的地址复制给一个局部变量，因此这个局部变量的变化会反映在函数外部。

                4、检测类型
                    要检测一个变量是不是基本数据库类型，typeof操作符是最佳工具。具体一点，就是typeof操作符是确定一个变量是字符串、数值、布尔值，还是undefined的最佳工具
                    如果一个变量的值是null，而typeof操作符会返回object。而typeof在检测引用类型值时，都返回object，并不知道他是什么类型的对象。为此，ECMAScript提供了
                    instanceof操作符，其语法如下：
                        result = variable instanceof constoructor

                    如果变量是给定引用类型（根据其原型链来识别）的实例，那么instanceof操作符就返回true。根据规定，所有引用类型的值都是Object的实例，因此在检测一个引用类型值
                    和Object构造函数时，instanceof操作符始终会返回true。当然，如果使用instanceof操作符检测基本类型的值，则会始终返回false，因为基本类型不是对象。

            执行环境及作用域：
                执行环境（execution context，为简单起见，有时也称为环境）是JavaScript中最为重要的一个概念。执行环境定义了变量和函数有权访问的其他数据，决定了他们各自的行为。
                每个执行环境都有一个与之关联的变量对象（variable object），环境中定义的所有变量和函数都保存在这个对象中。虽然我们在编写代码时无法访问这个对象，但是解析器在处理
                数据时会在后台使用它。

                全局执行环境是最外围的一个执行环境，根据ECMAScript实现所在的宿主环境不同，表示执行环境的对象也不一样。在Web浏览器中，全局执行环境被认为是window对象，因此，所有
                全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后，该环境被销毁，保存在其中的所有变量和函数定义也会随之销毁（全局执行环境直到
                应用程序退出--例如关闭网页或浏览器时才被销毁）

                每个函数都有自己的执行环境，当执行流进入一个函数时，函数的环境就会被推入一个环境栈中，而在函数执行后，栈将其环境弹出，把控制权返回给之前的执行环境。ECMAScript程序中
                的执行流正式由于这个方便的机制控制着。

                当代码在一个环境中执行时，会创建变量对象的一个作用域链（scope chain）。作用域链的用途，是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端，始终都是
                当前执行的代码所在环境的变量对象。如果这个环境是函数，则将其活动对象（activation object）作为变量对象。活动对象在最开始只包含一个变量，即arguments对象（这个对象在
                全局环境中是不存在的）。作用域链中的下一个变量对象来自包含（外部）环境，而再下一个变量对象来自下一个包含环境。这样，一直延续到全局执行环境。全局执行环境的变量对象始终
                都是作用域链中的最后一个对象。

                标识符解析是沿着作用域链一级一级的搜索标识符的过程。搜索过程中始终从作用域链的前端开始，然后逐级的向后回溯，知道找到标识符为止（如果找不到标识符，通常就会发生错误）。
                
                1、延长作用域链
                    虽然执行环境的类型总共只有两种：全局和局部（函数），但还是有其他办法来延长作用域链。这么说是因为有些语句可以在作用域链的前端临时增加一个变量对象，该变量对象会在代码
                    执行后被移除。在两种情况下会发生这种现象。具体来说，就是当执行流进入下列任何一个语句时，作用域链就会得到加长：
                        try-catch语句的catch块；
                        with语句
                    
                    这两个语句都会在作用域链的前端添加一个变量对象。对with语句来说，会将指定的对象添加到作用域链中。对catch语句来说，会创建一个新的变量对象，其中包含的是被抛出的错误
                    对象的声明。

        */
    </script>
</body>
</html>